package MathCore.Operators;

import MathCore.Lexeme;
import MathCore.Operand;
import MathCore.Operands.List;
import MathCore.Operands.Number;
import MathCore.Real;

public class TrigFunction extends Function
{
	public static final int SIN		=0;
	public static final int COS		=1;
	public static final int TG		=2;
	public static final int CTG		=3;
	public static final int ARCSIN	=4;
	public static final int ARCCOS	=5;
	public static final int ARCTG	=6;
	public static final int ARCCTG	=7;

	public final static int ANGLE_DEGREE = 0,  ANGLE_GRAD = 2,  ANGLE_RADIAN = 1;
	public static int angleType = ANGLE_RADIAN;

	public TrigFunction(int type)
	{
		this.type = type;
	}
	
	public TrigFunction(TrigFunction original)
	{
		type = original.type;
	}

	public Operand exec(Operand[] operands) throws OperatorException
	{
		if(operands.length != params())
			throw new OperatorException("Неправильное количество операндов у функции "+toString()+". Необходимо "+params()+".");

		if(have(operands, List.class)) return expandListsLinear(operands, this);
		else if(only(operands, Number.class))
		{
			Number result = new Number((Number) operands[0]);

			switch(type)
			{
				case SIN:
					convertAngleToRadians(result.get());
					result.get().sin();
					break;

				case COS:
					convertAngleToRadians(result.get());
					result.get().cos();
					break;

				case TG:
					convertAngleToRadians(result.get());
					result.get().tan();
					break;

				case CTG:
					convertAngleToRadians(result.get());
					result.get().tan();
					result.get().rdiv(1);
					break;

				case ARCSIN:
					result.get().asin();
					convertAngleFromRadians(result.get());
					break;

				case ARCCOS:
					result.get().acos();
					convertAngleFromRadians(result.get());
					break;

				case ARCTG:
					result.get().atan();
					convertAngleFromRadians(result.get());
					break;

				case ARCCTG:
					result.get().rdiv(1);
					result.get().atan();
					convertAngleFromRadians(result.get());
					break;
			}

			return result;
		}
		else
		{
			throw new OperatorException(OperatorException.UNSUPPORTED_COMBINATION);
		}
	}

	public int params()
	{
		return 1;
	}

	public String toString()
	{
		switch(type)
		{
			case SIN:
				return "sin";

			case COS:
				return "cos";

			case TG:
				return "tg";

			case CTG:
				return "ctg";

			case ARCSIN:
				return "arcsin";

			case ARCCOS:
				return "arccos";

			case ARCTG:
				return "arctg";

			case ARCCTG:
				return "arcctg";
		}

		return "";
	}

	public Lexeme clone()
	{
		return new TrigFunction(this);
	}

	// Преобразование параметра-угла к радианам
	public static void convertAngleToRadians(Real var)
	{
		if (angleType == ANGLE_DEGREE)
		{
			var.mul(Real.PI);
			var.div(180);
		}
		else if (angleType == ANGLE_GRAD)
		{
			var.mul(Real.PI);
			var.div(200);
		}
		else
		{
			angleType = ANGLE_RADIAN;
		}

	}

	// Преобразование результата обратной триг. ф-ии к требуемому углу
	public static void convertAngleFromRadians(Real var)
	{
		if (angleType == ANGLE_DEGREE)
		{
			var.div(Real.PI);
			var.mul(180);
		}
		else if (angleType == ANGLE_GRAD)
		{
			var.div(Real.PI);
			var.mul(200);
		}
		else
		{
			angleType = ANGLE_RADIAN;
		}
	}
}
